import random
import re
from flask import abort, jsonify
from flask import current_app
from flask import make_response
from flask import request
from info import redis_store
from info.libs.yuntongxun.sms import CCP
from info.utils.response_code import RET
from info.utils.captcha.captcha import captcha  # 用点来导入错误，Parent module '' not loaded, cannot perform relative import
from info.modules.passport import passport_blu
from datetime import datetime
from flask import json, session
from info import constants, db
from info.models import User


# 登陆按钮路由
@passport_blu.route("/login", methods=["POST"])
def login():
    """
    1.获取登陆参数
    2. 校验登陆参数
    3.校验密码是否正确
    4.保存用户的登陆状态到session
    5.响应
    ps:该做的校验都要做不能省防止模拟前端的攻击
    """
    # 1.获取参数
    params_dict = request.json
    mobile = params_dict.get("mobile")
    password = params_dict.get("password")

    # 2. 校验参数
    if not all([params_dict, mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
    # 验证手机号码是否正确
    if not re.match('1[35678]\\d{9}', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式不正确")
    # 3.校验密码是否正确
    # 先查询数据库中是否有指定手机号码的用户
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据失败")
    # 判断用户是否存在
    if not user:
        return jsonify(errno=RET.NODATA, errmsg="用户不存在")

    # 利用模板中判断haxi的方法判断密码是否正确
    if not user.check_password(password):
        return jsonify(errno=RET.PWDERR, errmsg="用户名或者密码错误")

    # 4. 保存用户的登录状态
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name

    # 4.5设置当前用户最后一次登陆时间
    # 设置了数据库里面的数据,照理说要commit但是设置了数据库配置,所以直接去commit了
    user.last_login = datetime.now()


    # 5. 响应
    print("1111")
    return jsonify(errno=RET.OK, errmsg="登录成功")


# 发送短信按钮路由
@passport_blu.route('/sms_code', methods=["POST"])
def send_sms_code():
    """
    发送短信的逻辑
    1. 获取参数：手机号，图片验证码内容，图片验证码的编号 (随机值)
    2. 校验参数(参数是否符合规则，判断是否有值)
    3. 先从redis中取出真实的验证码内容
    4. 与用户的验证码内容进行对比，如果对比不一致，那么返回验证码输入错误
    5. 如果一致，生成验证码的内容(随机数据)
    6. 发送短信验证码
    7. 告知发送结果
    """
    # '{"mobiel": "18811111111", "image_code": "AAAA", "image_code_id": "u23jksdhjfkjh2jh4jhdsj"}'
    # 1,获取参数，手机号，图片验证码内容，图片验证码编号
    # 将返回的字符串转换成json 字典
    params_dict = request.json
    mobile = params_dict.get("mobile")
    image_code = params_dict.get("image_code")
    image_code_id = params_dict.get("image_code_id")

    # 2，校验参数是否符合规则是否正确
    # 判断参数是否有值
    if not all([mobile, image_code, image_code_id]):
        return jsonify(eerrno=RET.PARAMERR, errmsg="参数有误")

    # 判断手机号码是否符合规则
    if not re.match("1[35678]\\d{9}", mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号码格式不正确")

    # 3,从redis中取出图片验证码内容
    try:
        real_image_code = redis_store.get("ImageCodeId_" + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据查询失败")
    if not real_image_code:
        return jsonify(errno=RET.NODATA, errmsg="图片验证码已过期")

    # current_app.logger.debug(real_image_code)
    # current_app.logger.error(image_code)
    # 将消息放到调试界面中显示
    # 4,与用户验证码内容进行对比，如果对比不一致，返回验证码输入错误，

    if real_image_code.upper() != image_code.upper():
        return jsonify(errno=RET.DATAERR, errmsg="输入图形验证码错误")
    # 5,如果对比一致，随机生成短信验证吗的内容
    sms_code_str = "%06d"% random.randint(0, 999999)
    current_app.logger.debug("短信验证码内容是：%s" % sms_code_str)

    # 6,发送短信验证码
    result = CCP().send_template_sms(mobile, [sms_code_str, constants.SMS_CODE_REDIS_EXPIRES/5], "1")
    if result != 0:
        # 模板中只设置了给一个手机发短信
        # 状态码0代表发送成功
        return jsonify(errno=RET.THIRDERR, errmsg="发送短信失败")

    # 7,保存验证码内容到redis
    try:
        redis_store.set("SMS_" + mobile, sms_code_str, constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="保存数据失败")

    # 8,告知发送结果
    return jsonify(errno=RET.OK, errmsg="发送成功")


#################################################################################################################
# @passport_blu.route('/sms_code', methods=["POST"])
# def send_sms_code():
#     """
#     发送短信的逻辑
#     1. 获取参数：手机号，图片验证码内容，图片验证码的编号 (随机值)
#     2. 校验参数(参数是否符合规则，判断是否有值)
#     3. 先从redis中取出真实的验证码内容
#     4. 与用户的验证码内容进行对比，如果对比不一致，那么返回验证码输入错误
#     5. 如果一致，生成验证码的内容(随机数据)
#     6. 发送短信验证码
#     7. 告知发送结果
#     :return:
#     """
#     # '{"mobiel": "18811111111", "image_code": "AAAA", "image_code_id": "u23jksdhjfkjh2jh4jhdsj"}'
#     # 1. 获取参数：手机号，图片验证码内容，图片验证码的编号 (随机值)
#     # params_dict = json.loads(request.data)
#     params_dict = request.json
#
#     mobile = params_dict.get("mobile")
#     image_code = params_dict.get("image_code")
#     image_code_id = params_dict.get("image_code_id")
#
#     # 2. 校验参数(参数是否符合规则，判断是否有值)
#     # 判断参数是否有值
#     if not all([mobile, image_code, image_code_id]):
#         # {"errno": "4100", "errmsg": "参数有误"}
#         return jsonify(errno=RET.PARAMERR, errmsg="参数有误")
#     # 校验手机号是否正确
#     if not re.match('1[35678]\\d{9}', mobile):
#         return jsonify(errno=RET.PARAMERR, errmsg="手机号格式不正确")
#
#     # 3. 先从redis中取出真实的验证码内容
#     try:
#         real_image_code = redis_store.get("ImageCodeId_" + image_code_id)
#     except Exception as e:
#         current_app.logger.error(e)
#         return jsonify(errno=RET.DBERR, errmsg="数据查询失败")
#
#     if not real_image_code:
#         return jsonify(errno=RET.NODATA, errmsg="图片验证码已过期")
#
#     print(real_image_code.upper())
#     print(image_code.upper())
#
#     # 4. 与用户的验证码内容进行对比，如果对比不一致，那么返回验证码输入错误
#     if real_image_code.upper() != image_code.upper():
#         return jsonify(errno=RET.DATAERR, errmsg="图形验证码输入错误")
#
#     # 5. 如果一致，生成短信验证码的内容(随机数据)
#     # 随机数字 ，保证数字长度为6位，不够在前面补上0
#     sms_code_str = "%06d" % random.randint(0, 999999)
#     current_app.logger.debug("短信验证码内容是：%s" % sms_code_str)
#     # 6. 发送短信验证码
#     # result = CCP().send_template_sms(mobile, [sms_code_str, constants.SMS_CODE_REDIS_EXPIRES / 5], "1")
#     # if result != 0:
#     #     # 代表发送不成功
#     #     return jsonify(errno=RET.THIRDERR, errmsg="发送短信失败")
#     #
#     # # 保存验证码内容到redis+
#     # try:
#     #     redis_store.set("SMS_" + mobile, sms_code_str, constants.SMS_CODE_REDIS_EXPIRES)
#     # except Exception as e:
#     #     current_app.logger.error(e)
#     #     return jsonify(errno=RET.DBERR, errmsg="数据保存失败")
#
#     # 7. 告知发送结果
#     return jsonify(errno=RET.OK, errmsg="发送成功")
################################################################################################################


# 获取图片验证码路由


# 获取图片验证码按钮路由
@passport_blu.route('/image_code')
def get_image_code():
    """获取图片验证码路由,前端点击图片获得"""
    # 1,获取参数
    # args 取到url？后面的参数
    image_code_id = request.args.get("imageCodeId", None)  # 随机生成的id

    # 2,判断参数是否有值
    if not image_code_id:
        return abort(403)

    # 3,生成图片验证码，拆包生成验证码函数
    name, text, image = captcha.generate_captcha()

    # 4,保存图片验证码文字内容到redies
    try:
        redis_store.set("ImageCodeId_" + image_code_id, text, constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        abort(500)

    # 5,返回验证码图片
    response = make_response(image)
    response.headers["Content-Type"] = "image/jpg"
    return response


# 注册按钮路由
@passport_blu.route("/register", methods=["POST"])
def register():
    """注册逻辑
    1.获取参数
    2.校验参数
    3.取到服务器保存的真实的验证码内容
    4.校验取到的验证码内容是否和真实的验证码内容一致
    5.如果一致,初始化user模型,并且赋值属性
    6.将user模型添加数据库
    7.返回响应
    """
    # 1.获取参数,从请求中获取数据转换成json格式
    param_dict = request.json
    mobile = param_dict.get("mobile")
    smscode = param_dict.get("smscode")
    password = param_dict.get("password")

    # 2.all函数校验参数是否有值
    if not all([mobile, smscode, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不能为空")

    # 3.校验手机号是否正确

    if not re.match('1[35678]\\d{9}', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式不正确")

    # 4.取到服务器保存的真实验证码
    try:
        real_sms_code = redis_store.get("SMS_" + mobile)
    except Exception as e:
        return jsonify(errno=RET.DBERR, errmsg="数据查询失败")

    if not send_sms_code:
        return jsonify(errno=RET.NODATA, errmsg="验证码已过期")

    # 5.校验用户输入的短信验证码和真实内容是否一致
    current_app.logger.debug("真实的:%s"%real_sms_code)
    current_app.logger.debug("取到的:%s"%smscode)

    if real_sms_code != smscode:
        return jsonify(errno=RET.DATAERR, errmsg="输入短信验证码错误")
    # 6.如果一致,初始化user模型,并且给表添加数据
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    # 记录用户最后一次登陆的时间
    user.last_login = datetime.now()
    # TODO对密码做处理
    # 需求：在设置 password 的时候，去对 password 进行加密，并且将加密结果给 user.password_hash 赋值
    user.password = password

    # 7.添加数据到数据库
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存数据失败")

    # 8.往session中保存数据表示当前已经登陆过,可直接下次登陆显示用户名等信息
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name
    # 9.返回响应
    return jsonify(errno=RET.OK, errmsg="注册成功")


# 退出登陆按钮路由
@passport_blu.route("/logout")
def logout():
    # 退出登陆逻辑,移除 session中的值
    # pop是移除session中的数据dict,pop会有返回值,如果移除的key不存在,旧返回none
    session.pop('user_id', None)
    session.pop('mobile', None)
    session.pop('nick_name', None)
    # 要清楚is_admin的值，如果不清除，先登录管理员，会保存到session，再登录普通用户，又能访问管理员页面
    session.pop("is_admin", None)
    return jsonify(errno=RET.OK, errmsg="退出成功")
    # 前段接受中ajax接受数据局部刷新
